use core::{marker::PhantomData, ptr::copy_nonoverlapping, slice};
use cmsis_rtos2_rs::{osWaitForever, static_mem::EventFlags};
use proto_bytes::{message::MessageRef, proto::{xor_sum, END, HEAD0, HEAD1}, proto2::ToProtoBytes2};
use utils::{ ufmt, ByteBuffer, MutCell, VolatileHandle};

#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(ufmt::derive::uDebug)]
pub enum Error {
    BufferSize,
    CheckedSum,
    PkgEnd,
    Timeout,
}

pub trait Uart {
    fn rx_index() -> usize;
    fn write_with_dma(write_buf: &[u8]);
    fn recv_with_dma(recv_buf: &[u8]);
}

const FLAG_TC: u32 = 0x01;
const FLAG_IDLE: u32 = 0x02;

pub struct UartContext<const WRITE_BUF_SIZE: usize, const RECV_BUF_SIZE: usize, U: Uart> {
    event_flags: EventFlags,
    write_buf: ByteBuffer<WRITE_BUF_SIZE>,
    recv_buf: ByteBuffer<RECV_BUF_SIZE>,
    recv_index: MutCell<usize>,
    _u: PhantomData<U>,
}

impl<const WRITE_BUF_SIZE: usize, const RECV_BUF_SIZE: usize, U: Uart>
    UartContext<WRITE_BUF_SIZE, RECV_BUF_SIZE, U>
{
    pub const fn uninit() -> Self {
        Self {
            event_flags: EventFlags::uninit(),
            write_buf: ByteBuffer::const_new(),
            recv_buf: ByteBuffer::const_new(),
            recv_index: MutCell::const_new(0),
            _u: PhantomData,
        }
    }

    pub unsafe fn init_once(&self, name: &'static str) {
        self.event_flags.init_once(name);
        self.event_flags.set_flags(FLAG_TC);
        U::recv_with_dma(unsafe { self.recv_buf.as_mut_slice() });
    }

    pub unsafe fn write<T: ToProtoBytes2>(&self, x: T) {
        _ = self.event_flags.wait_any_flags(FLAG_TC, osWaitForever);
        let ptr = self.write_buf.as_mut_ptr();
        let len = x.to_bytes(ptr);
        U::write_with_dma(slice::from_raw_parts(ptr, len));
    }

    pub fn wait_write_finish(&self) {
        _ = self.event_flags.wait_any_flags(FLAG_TC, osWaitForever);
        self.event_flags.set_flags(FLAG_TC);
    }

    fn sync(&self) -> u8 {
        let mut flag = false;
        let mut buf = [0u8];
        loop {
            self.read(&mut buf, osWaitForever);
            if flag && (buf[0] == HEAD1) {
                break;
            }
            flag = buf[0] == HEAD0;
        }
        self.read(&mut buf, osWaitForever);
        buf[0]
    }

    pub fn read_line<'a>(&self, buf: &'a mut [u8], timeout: u32) -> Result<&'a [u8], Error> {
        let mut index = 0;
        let len = 0;

        loop {
            if index >= len {
                return Err(Error::BufferSize);
            }

            let n = self.read(&mut buf[index..], timeout);
            if n == 0 {
                return Err(Error::Timeout);
            }
            index += n;

            if (index >= 2) && (&buf[..index] == b"\r\n") {
                break;
            }
        }

        Ok(&buf[..(index - 2)])
    }

    pub fn read_message<'a>(&self, buf: &'a mut [u8]) -> Result<MessageRef<'a>, Error> {
        if buf.len() < 5 {
            return Err(Error::BufferSize);
        }

        buf[0] = HEAD0;
        buf[1] = HEAD1;
        let head3 = self.sync();
        buf[2] = head3;

        let len = head3 as usize;
        if buf.len() < len {
            return Err(Error::BufferSize);
        }

        let mut index = 3;
        while index < len {
            let n = self.read(&mut buf[index..len], 100);
            if n == 0 {
                return Err(Error::Timeout);
            }
            index += n;
        }

        let s = xor_sum(&buf[3..(len - 2)]);
        if s != buf[len - 2] {
            return Err(Error::CheckedSum);
        }

        if END != buf[len - 1] {
            return Err(Error::PkgEnd);
        }

        Ok(unsafe { MessageRef::unchecked_new(&buf[..len]) })
    }

    fn read(&self, buf: &mut [u8], timeout: u32) -> usize {
        let mut recv_index = self.recv_index.volatile_read();
        let mut index = 0;
        let len = buf.len();

        while index < len {
            let mut n = len - index;
            let rx_index = U::rx_index();
            let src = unsafe { self.recv_buf.as_ptr().add(recv_index) };
            let dst = unsafe { buf.as_mut_ptr().add(index) };

            let max_recv_len = if rx_index > recv_index {
                rx_index - recv_index
            } else if rx_index < recv_index {
                RECV_BUF_SIZE - recv_index
            } else {
                if let Err(_) = self.event_flags.wait_any_flags(FLAG_IDLE, timeout) {
                    break;
                } else {
                    continue;
                }
            };

            if n >= max_recv_len {
                n = max_recv_len;
            }
            unsafe {
                copy_nonoverlapping(src, dst, n);
            }

            recv_index = (recv_index + n) % RECV_BUF_SIZE;
            index += n;
        }
        unsafe {
            self.recv_index.as_mut().volatile_write(recv_index);
        }
        index
    }

    #[inline(always)]
    pub fn on_tc(&self) {
        self.event_flags.set_flags(FLAG_TC);
    }

    #[inline(always)]
    pub fn on_idle(&self) {
        self.event_flags.set_flags(FLAG_IDLE);
    }
}
